/*******************************************************************************
 * Copyright (c) 2001, 2004 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package com.trainfinder.datapersistace;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.NonUniqueObjectException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.StaleStateException;
import org.hibernate.Transaction;

import com.trainfinder.requestor.IJourney;
import com.trainfinder.requestor.JourneyPk;

final class  BeanStorageImpl implements BeanStorage {

	public static final SessionFactory SESSION_FACTORY;

	private static final Log LOG = LogFactory.getLog(BeanStorageImpl.class);

	private static final long ONE_DAY = 24 * 60 * 60 * 1000;

	static {
		SessionFactory sf;

		try {
			sf = BeanSessionFactory.newSessionFactory();
		} catch (Exception e) {
			// e.printStackTrace();
			StringWriter sw = new StringWriter();
			PrintWriter pw = new PrintWriter(sw, true);
			e.printStackTrace(pw);
			pw.flush();
			sw.flush();
			sf = null;
			LOG.info("StackTrace :\n" + sw.toString());
		}
		SESSION_FACTORY = sf;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.trainfinder.BeanStorage#store(com.trainfinder.Journey)
	 */
	// public synchronized void store(IJourney bean) {
	// Session s;
	// Transaction tx;
	// s = sessionFactory.openSession();
	// tx = s.beginTransaction();
	// s.saveOrUpdate(bean);
	// tx.commit();
	// s.close();
	// }
	public void storeAndMerge(Collection<IJourney> beans)
			throws DataPersitanceException {

		for (IJourney newJourney : beans) {

			Session s;
			Transaction tx;
			s = SESSION_FACTORY.openSession();
			tx = s.beginTransaction();

			final Query q = s.getNamedQuery("get.single.journey");

			q.setParameter("date", newJourney.getId().getDate());
			q.setParameter("outwardOrReturn", newJourney.getId()
					.getOutwardOrReturn());
			q.setParameter("ticketClass", newJourney.getId().getTicketClass());
			q.setParameter("ticketType", newJourney.getId().getTicketType());

			try {

				final List list = q.list();

				if (list.size() > 1) {

					for (Iterator iter = list.iterator(); iter.hasNext();) {
						IJourney element = (IJourney) iter.next();
						// System.out.println(element);

					}
					throw new DataPersitanceException(
							"Expected a single item to be returned but got "
									+ list.size() + " items");
				}

				if (list.size() == 1) {

					final IJourney restoredJourney = (IJourney) q.list().get(0);
					final IJourney mergedJourney = mergeJournies(newJourney,
							restoredJourney);
					try {
						s.update(mergedJourney);
						System.out.println(mergedJourney);
					} catch (NonUniqueObjectException e) {
						// do nothing
					}

				} else {
					System.out.println(newJourney.toString());
					s.saveOrUpdate(newJourney);
				}
			} catch (StaleStateException e) {
				// e.printStackTrace();
				// s.saveOrUpdate(newJourney);
			}

			tx.commit();
			s.close();

		}

	}

	private IJourney mergeJournies(IJourney newJourney, IJourney resultJourney) {

		if (newJourney.hasSetWeekdayPrice()) {
			resultJourney.setWeekdayPence(newJourney.getWeekdayPence());
		}

		if (newJourney.hasSetWeekendPrice()) {
			resultJourney.setWeekendPence(newJourney.getWeekendPence());
		}

		if (resultJourney.getLastUpdatedWeekdayPrice().getTime() < (new Date()
				.getTime() - ONE_DAY)) {
			resultJourney.setWeekdayPence(0);
		}

		if (resultJourney.getLastUpdatedWeekendPrice().getTime() < (new Date()
				.getTime() - ONE_DAY)) {
			resultJourney.setWeekendPence(0);
		}

		return resultJourney;
	}

	public IJourney retreive(JourneyPk journeyPk)
			throws DataPersitanceException {

		Session s;
		Transaction tx;
		s = SESSION_FACTORY.openSession();
		tx = s.beginTransaction();

		final Query q = s.getNamedQuery("get.single.journey");

		q.setParameter("date", journeyPk.getDate());
		q.setParameter("outwardOrReturn", journeyPk.getOutwardOrReturn());
		q.setParameter("ticketClass", journeyPk.getTicketClass());
		q.setParameter("ticketType", journeyPk.getTicketType());

		List list = q.list();
		if (list.size() > 1) {
			throw new DataPersitanceException(
					"Expected a single item to be returned but got "
							+ list.size() + " items");
		}

		final IJourney restoredJourney = (IJourney) q.list().get(0);

		tx.commit();
		s.close();

		return restoredJourney;

	}

	public List retreive(Date afterDate, String fromStation,
			String toStation, TicketType ticketType) {

		Session s;
		Transaction tx;
		s = SESSION_FACTORY.openSession();
		tx = s.beginTransaction();

		final Query q = s.getNamedQuery("get.journey");

		final Date dateTo = new Date(afterDate.getTime()
				+ (1000L * 60 * 60 * 24 * 14));
		q.setParameter("dateAfter", afterDate);
		q.setParameter("fromStation", fromStation);
		q.setParameter("toStation", toStation);
		q.setParameter("dateTo", dateTo);
		q.setParameter("ticketType", Integer.valueOf(ticketType.ordinal()));

		List result = q.list();

		tx.commit();
		s.close();

		return result;

	}

	public void removeExpiredData(Date date) {
		Session s;
		Transaction tx;
		s = SESSION_FACTORY.openSession();
		tx = s.beginTransaction();

		final Query q = s.getNamedQuery("remove-expired-data");
		q.setParameter("date", new Date());

		final List result = q.list();

		for (Iterator iter = result.iterator(); iter.hasNext();) {
			Object o = iter.next();
			if (o != null) {
				s.delete(o);
			}
		}

		tx.commit();
		s.close();

	}

	public final void removeDataWithOldLastUpdatedTime(
			String fromLocation, String toLocation, Date lastUpdated) {
		Session s;
		Transaction tx;
		s = SESSION_FACTORY.openSession();
		tx = s.beginTransaction();

		final Query q = s.getNamedQuery("remove-old-lastupdated");

		q.setParameter("lastUpdated", lastUpdated);
		q.setParameter("fromStation", fromLocation);
		q.setParameter("toStation", toLocation);

		final List result = q.list();

		for (Iterator iter = result.iterator(); iter.hasNext();) {
			s.delete(iter.next());
		}

		tx.commit();
		s.close();

	}

	
	public void close() {
		SESSION_FACTORY.close();
	}

}
